package com.atguigu.spzx.manager.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import com.alibaba.fastjson.JSON;
import com.atguigu.spzx.manager.interceptor.LoginAuthInterceptor;
import com.atguigu.spzx.manager.mapper.SysUserMapper;
import com.atguigu.spzx.manager.mapper.SysUserRoleMapper;
import com.atguigu.spzx.manager.service.SysUserService;
import com.atguigu.spzx.model.dto.sys.LoginDTO;
import com.atguigu.spzx.model.dto.sys.SysUserDTO;
import com.atguigu.spzx.model.entity.sys.SysUser;
import com.atguigu.spzx.model.exception.SpzxException;
import com.atguigu.spzx.model.vo.common.ResultCodeEnum;
import com.atguigu.spzx.model.vo.sys.LoginVO;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class SysUserServiceImpl implements SysUserService {
    @Resource
    SysUserMapper sysUserMapper;
    @Resource
    SysUserRoleMapper sysUserRoleMapper;
    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Override
    public LoginVO login(LoginDTO loginDTO) {
        //1、参数校验
        String userName = loginDTO.getUserName();
        String password = loginDTO.getPassword();
        String code = loginDTO.getCode();
        String codeKey = loginDTO.getCodeKey();
        if(StringUtils.isEmpty(userName)||
                StringUtils.isEmpty(password)||
                StringUtils.isEmpty(code)||
                StringUtils.isEmpty(codeKey)
        ){
            throw new SpzxException(ResultCodeEnum.PARAMS_ERROR);
        }
        //1.5、验证码校验
        //获取redis中的缓存验证码
        String cachedCode = stringRedisTemplate.opsForValue().get("sys:user:code:" + codeKey);
        if(StringUtils.isEmpty(cachedCode) ||
                !cachedCode.equals(code)){
            throw new SpzxException(ResultCodeEnum.CODE_ERROR);
        }
        //移除验证码缓存：
        stringRedisTemplate.delete("sys:user:code:" + codeKey);
        //2、判断账号是否存在
        SysUser sysUser =  sysUserMapper.selectSysUserByUsername(loginDTO.getUserName());
        if(sysUser==null){
            throw new SpzxException(ResultCodeEnum.LOGIN_USERNAME_ERROR);
        }
        //3、判断密码是否正确： 密码加盐
        // MD5加密的密码不安全：md5属于不可逆加密，加密的结果无法还原，可以被暴力破解(相同的内容加密的结果一致)

        //对登录的明文密码加密 得到的结果 和数据库查询到的密文比较
        String encodePwd = DigestUtils.md5DigestAsHex(password.getBytes());
        if(!encodePwd.equals(sysUser.getPassword())){
            throw new SpzxException(ResultCodeEnum.LOGIN_PASSWORD_ERROR);
        }
        // Integer默认缓存的范围为-127~128
        if(1!=sysUser.getStatus().intValue()){
            throw  new SpzxException(ResultCodeEnum.LOGIN_ACCOUNT_LOCKED_ERROR);
        }

        //4、保持登录状态到redis
        String token = UUID.randomUUID().toString().replace("-","");
        //sysUser对象最好脱敏后存入到redis中：
        sysUser.setPassword(null);
        //redis中的缓存的数据较多，以后如果要管理键值对，redis建议数据键使用 :分隔，在图形化工具中 自动形成目录分组
        stringRedisTemplate.opsForValue().set("sys:user:login:"+token , JSON.toJSONString(sysUser) ,
                7, TimeUnit.DAYS);

        //5、封装LoginVO
        LoginVO loginVO = new LoginVO();
        loginVO.setToken(token);
        //前端不用
        loginVO.setRefresh_token("1");
        return loginVO;
    }

    @Override
    public Map<String, Object> generateCodeImg() {
        CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(180, 40, 4, 20);
        String img = captcha.getImageBase64();
        String code = captcha.getCode();
        //缓存code到redis中
        String uuid = UUID.randomUUID().toString().replace("-", "");
        stringRedisTemplate.opsForValue().set("sys:user:code:"+uuid , code,15, TimeUnit.MINUTES);

        Map<String, Object> map = new HashMap<>();
        map.put("image","data:image/jpeg;base64,"+img);
        map.put("codeKey" , uuid);
        return map;
    }

    @Override
    public Map<String, Object> getUserInfo() {
        SysUser sysUser = LoginAuthInterceptor.getSysUser();
        //可以将k-v结构的对象互相转换
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> map = mapper.convertValue(sysUser, Map.class);
        return map;
    }

    @Override
    public void logout(String token) {
        stringRedisTemplate.delete("sys:user:login:" + token);
    }

    @Override
    public PageInfo<SysUser> list(Integer pageNum, Integer pageSize, SysUserDTO sysUserDTO) {
        PageHelper.startPage(pageNum,pageSize);
        //select * from table limit index,size;
        List<SysUser> sysUsers =  sysUserMapper.selectByCondition(sysUserDTO);
        return new PageInfo<>(sysUsers);
    }

    @Override
    public void save(SysUser sysUser) {
        //sysUser：
        //用户账号唯一性校验：

        //密码需要处理：加密
        String password = sysUser.getPassword();
        String encodePwd = DigestUtils.md5DigestAsHex(password.getBytes());
        sysUser.setPassword(encodePwd);
        sysUserMapper.save(sysUser);
    }

    @Override
    public void deleteByIds(List<Long> ids) {
        sysUserMapper.deleteByIds(ids);
    }

    @Override
    public SysUser getById(Long id) {
        return sysUserMapper.selectById(id);
    }

    @Override
    public void update(SysUser sysUser) {
        //账号唯一性校验
        //更新时不会更新密码
        sysUserMapper.update(sysUser);
    }

    @Override
    public List<SysUser> list() {
        return sysUserMapper.selectByCondition(null);
    }

    @Override
    public void assignRoles2User(Long id, List<Long> roleIds) {
        //1、先删除用户的所有角色：物理删除 sys_user_role
        sysUserRoleMapper.deleteByUserId(id);
        if(CollectionUtils.isEmpty(roleIds)){
            return;
        }
        //2、分配角色
        sysUserRoleMapper.assignRoles2User(id, roleIds);
    }
}
